/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
/* JOrbis
 * Copyright (C) 2000 ymnk, JCraft,Inc.
 *  
 * Written by: 2000 ymnk<ymnk@jcraft.com>
 *   
 * Many thanks to 
 *   Monty <monty@xiph.org> and 
 *   The XIPHOPHORUS Company http://www.xiph.org/ .
 * JOrbis has been based on their awesome works, Vorbis codec.
 *   
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public License
 * as published by the Free Software Foundation; either version 2 of
 * the License, or (at your option) any later version.

 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Library General Public License for more details.
 * 
 * You should have received a copy of the GNU Library General Public
 * License along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

package com.jcraft.jorbis;

class Lpc
{
	// en/decode lookups
	Drft fft = new Drft();;

	int ln;
	int m;

	// Autocorrelation LPC coeff generation algorithm invented by
	// N. Levinson in 1947, modified by J. Durbin in 1959.

	// Input : n elements of time doamin data
	// Output: m lpc coefficients, excitation energy

	static float lpc_from_data(float[] data, float[] lpc, int n, int m)
	{
		float[] aut = new float[m + 1];
		float error;
		int i, j;

		// autocorrelation, p+1 lag coefficients

		j = m + 1;
		while (j-- != 0)
		{
			float d = 0;
			for (i = j; i < n; i++)
				d += data[i] * data[i - j];
			aut[j] = d;
		}

		// Generate lpc coefficients from autocorr values

		error = aut[0];
		/*
		 * if(error==0){ for(int k=0; k<m; k++) lpc[k]=0.0f; return 0; }
		 */

		for (i = 0; i < m; i++)
		{
			float r = -aut[i + 1];

			if (error == 0)
			{
				for (int k = 0; k < m; k++)
					lpc[k] = 0.0f;
				return 0;
			}

			// Sum up this iteration's reflection coefficient; note that in
			// Vorbis we don't save it. If anyone wants to recycle this code
			// and needs reflection coefficients, save the results of 'r' from
			// each iteration.

			for (j = 0; j < i; j++)
				r -= lpc[j] * aut[i - j];
			r /= error;

			// Update LPC coefficients and total error

			lpc[i] = r;
			for (j = 0; j < i / 2; j++)
			{
				float tmp = lpc[j];
				lpc[j] += r * lpc[i - 1 - j];
				lpc[i - 1 - j] += r * tmp;
			}
			if (i % 2 != 0)
				lpc[j] += lpc[j] * r;

			error *= 1.0 - r * r;
		}

		// we need the error value to know how big an impulse to hit the
		// filter with later

		return error;
	}

	// Input : n element envelope spectral curve
	// Output: m lpc coefficients, excitation energy

	float lpc_from_curve(float[] curve, float[] lpc)
	{
		int n = ln;
		float[] work = new float[n + n];
		float fscale = (float) (.5 / n);
		int i, j;

		// input is a real curve. make it complex-real
		// This mixes phase, but the LPC generation doesn't care.
		for (i = 0; i < n; i++)
		{
			work[i * 2] = curve[i] * fscale;
			work[i * 2 + 1] = 0;
		}
		work[n * 2 - 1] = curve[n - 1] * fscale;

		n *= 2;
		fft.backward(work);

		// The autocorrelation will not be circular. Shift, else we lose
		// most of the power in the edges.

		for (i = 0, j = n / 2; i < n / 2;)
		{
			float temp = work[i];
			work[i++] = work[j];
			work[j++] = temp;
		}

		return (lpc_from_data(work, lpc, n, m));
	}

	void init(int mapped, int m)
	{
		ln = mapped;
		this.m = m;

		// we cheat decoding the LPC spectrum via FFTs
		fft.init(mapped * 2);
	}

	void clear()
	{
		fft.clear();
	}

	static float FAST_HYPOT(float a, float b)
	{
		return (float) Math.sqrt((a) * (a) + (b) * (b));
	}

	// One can do this the long way by generating the transfer function in
	// the time domain and taking the forward FFT of the result. The
	// results from direct calculation are cleaner and faster.
	//
	// This version does a linear curve generation and then later
	// interpolates the log curve from the linear curve.

	void lpc_to_curve(float[] curve, float[] lpc, float amp)
	{

		for (int i = 0; i < ln * 2; i++)
			curve[i] = 0.0f;

		if (amp == 0)
			return;

		for (int i = 0; i < m; i++)
		{
			curve[i * 2 + 1] = lpc[i] / (4 * amp);
			curve[i * 2 + 2] = -lpc[i] / (4 * amp);
		}

		fft.backward(curve);

		{
			int l2 = ln * 2;
			float unit = (float) (1. / amp);
			curve[0] = (float) (1. / (curve[0] * 2 + unit));
			for (int i = 1; i < ln; i++)
			{
				float real = (curve[i] + curve[l2 - i]);
				float imag = (curve[i] - curve[l2 - i]);

				float a = real + unit;
				curve[i] = (float) (1.0 / FAST_HYPOT(a, imag));
			}
		}
	}
}
